/**
 * Created by IntelliJ IDEA.
 * User: Matjaz Urlep
 * Date: 2004.8.13
 * Time: 11:22:28
 * <p/>
 * (C) Genera Lynx, d.o.o.
 */

package com.generalynx.ecos.web.controller;

import java.util.HashMap;
import java.util.Iterator;
import java.util.List;
import java.util.Map;
import java.util.Set;
import java.util.TreeSet;
import javax.servlet.http.HttpServletRequest;
import javax.servlet.http.HttpServletResponse;

import com.alesj.newsfeed.data.Account;
import com.alesj.newsfeed.data.Contact;
import com.alesj.newsfeed.data.Group;
import com.alesj.newsfeed.data.GroupEntry;
import com.generalynx.common.constants.WebConst;
import com.generalynx.common.data.IEntity;
import com.generalynx.ecos.cache.ICacheBean;
import com.generalynx.ecos.data.Role;
import com.generalynx.ecos.data.User;
import com.generalynx.ecos.page.EmptyPageScroll;
import com.generalynx.ecos.page.PageScroll;
import com.generalynx.ecos.utils.HttpContext;
import com.generalynx.ecos.utils.IBindable;
import org.hibernate.LockMode;
import org.springframework.validation.ValidationUtils;
import org.springframework.validation.Validator;
import org.springframework.web.bind.RequestUtils;
import org.springframework.web.bind.ServletRequestDataBinder;
import org.springframework.web.servlet.ModelAndView;

/**
 * Add, edit, delete data controller.
 */
public class ADEController extends ExceptionMultiActionController {

    private ICacheBean m_cacheBean;
    private Validator m_validator;

    private Set cachePageScrollNonExistance = new TreeSet();

    public ICacheBean getCacheBean() {
        return m_cacheBean;
    }

    public void setCacheBean(ICacheBean cacheBean) {
        m_cacheBean = cacheBean;
    }

    public Validator getValidator() {
        return m_validator;
    }

    public void setValidator(Validator validator) {
        m_validator = validator;
    }

    private Map m_listMap = new HashMap();

    /* Defaults */

    //--- Helper functions ---

    private String getSessionId(HttpServletRequest request) {
        return request.getSession(false).getId();
    }

    private List put(HttpServletRequest request, List list) {
        return (List)m_listMap.put(getSessionId(request), list);
    }

    private List get(HttpServletRequest request) {
        return (List)m_listMap.get(getSessionId(request));
    }

    private List remove(HttpServletRequest request) {
        return (List)m_listMap.remove(getSessionId(request));
    }

    private IEntity getADEData(List list, int id) {
        Iterator iter = list.iterator();
        while (iter.hasNext()) {
            IEntity entity = (IEntity)iter.next();
            if (entity.getId() == id) {
                return entity;
            }
        }
        throw new IllegalArgumentException("List doesn't contain entity with id: " + id);
    }

    private void bind(HttpServletRequest request, IBindable target) throws Exception {
        ServletRequestDataBinder binder = new ServletRequestDataBinder(target, WebConst.DATA);
        initBinder(request, binder); // global default property editors
        target.registerCustomEditors(binder); // target specific property editors
        target.beforeBind();
        binder.bind(request);
        target.afterBind();
        Validator validator = target.validator();
        //if no specific validator use global
        if (validator == null) {
            validator = getValidator();
        }
        ValidationUtils.invokeValidator(validator, target, binder.getErrors());
        //binder.closeNoCatch(); //todo better error representation
    }

    private IBindable create(String className) throws Exception {
        Class clazz = Class.forName(className);
        IBindable bindable = (IBindable)clazz.newInstance();
        bindable.afterCreate();
        return bindable;
    }

    //--- Model & View

    private ModelAndView dataModelView(IEntity entity) {
        Map model = new HashMap();
        model.put(WebConst.DATA, entity);
        model.put("className", entity.getClass().getName());
        return new ModelAndView("adeeditView", model);
    }

    private ModelAndView listModelView(List list, Class clazz, PageScroll pageScroll)
            throws Exception {
        Map model = new HashMap();
        model.put(WebConst.LIST, list);
        model.put("subClasses", getCacheBean().findSubClasses(clazz));
        model.put("ps", pageScroll);
        return new ModelAndView("adelistView", model);
    }

    private ModelAndView msgModelView(String code) {
        return new ModelAndView("msgView", WebConst.MSG_CODE, code);
    }

    private ModelAndView deleteModelView(List list, int id, PageScroll pageScroll)
            throws Exception {
        IEntity entity = getADEData(list, id);
        getBasicDAO().deleteEntity(entity, LockMode.NONE);
        list.remove(entity);
        return listModelView(list, entity.getClass(), pageScroll);
    }

    private ModelAndView editModelView(List list, int id) {
        IEntity entity = getADEData(list, id);
        list.remove(entity); //to add new or existing which will be concurrent with Session
        return dataModelView(entity);
    }

    private ModelAndView saveModelView(IBindable entity, List list) {
        entity.beforeSaveOrUpdate(getApplicationContext());
        getBasicDAO().saveOrUpdateEntity(entity);
        list.add(entity);
        return msgModelView("saved");
    }

    private PageScroll getPageScroll(Class clazz){
        return getPageScroll(clazz.getName());
    }

    private PageScroll getPageScroll(String prefix){
        String name = prefix + ".PageScroll";
        if (cachePageScrollNonExistance.contains(name)) {
            return new EmptyPageScroll();
        } else {
            try {
                return (PageScroll)getApplicationContext().getBean(name);
            } catch (Exception e) {
                logger.warn("No PageScroll instance for: " + name);
                cachePageScrollNonExistance.add(name);
                return new EmptyPageScroll();
            }
        }
    }

    private ModelAndView handler(Class clazz, HttpServletRequest request) throws Exception {
        String sId = request.getParameter("id");
        int id = (sId != null && sId.length() > 0 ? Integer.parseInt(sId) : 0);
        String ps = request.getParameter("PageScroll");
        PageScroll pageScroll;
        if (ps != null) {
            pageScroll = getPageScroll(ps);
        } else {
            pageScroll = getPageScroll(clazz);
        }
        pageScroll.setClass(clazz);
        String action = request.getParameter("action");
        if (action == null) {
            return doPageScroll(pageScroll, clazz, request);
        } else if ("jump".equalsIgnoreCase(action)) {
            String identifier = RequestUtils.getRequiredStringParameter(request, "identifier");
            pageScroll.setCurrentIdentifier(identifier);
            return doPageScroll(pageScroll, clazz, request);
        } else if ("add".equalsIgnoreCase(action)) {
            String className = RequestUtils.getRequiredStringParameter(request, "className");
            return dataModelView(create(className));
        } else if ("delete".equalsIgnoreCase(action)) {
            return deleteModelView(get(request), id, pageScroll);
        } else if ("edit".equalsIgnoreCase(action)) {
            return editModelView(get(request), id);
        } else if ("save".equalsIgnoreCase(action)) {
            String className = RequestUtils.getRequiredStringParameter(request, "className");
            IBindable bindable;
            if (id > 0) {
                bindable = (IBindable)getBasicDAO().loadEntity(Class.forName(className), id);
            } else {
                bindable = create(className);
                bindable.beforeInsert(new HttpContext(request));
            }
            bind(request, bindable);
            return saveModelView(bindable, remove(request));
        } else {
            return new ModelAndView("welcomeView");
        }
    }

    private ModelAndView doPageScroll(PageScroll pageScroll,
                                      Class clazz,
                                      HttpServletRequest request) throws Exception {
        List list = getBasicDAO().findEntity(pageScroll);
        put(request, list);
        return listModelView(list, clazz, pageScroll);
    }

    /* List data views */

    public ModelAndView usersHandler(HttpServletRequest request, HttpServletResponse response) throws Exception {
        return handler(User.class, request);
    }

    public ModelAndView rolesHandler(HttpServletRequest request, HttpServletResponse response) throws Exception {
        return handler(Role.class, request);
    }

    // contacts by surname
    public ModelAndView contactsHandler(HttpServletRequest request, HttpServletResponse response) throws Exception {
        return handler(Contact.class, request);
    }

    // contacts by media
    public ModelAndView mediaHandler(HttpServletRequest request, HttpServletResponse response) throws Exception {
        return handler(Contact.class, request);
    }

    // contacts by email
    public ModelAndView emailHandler(HttpServletRequest request, HttpServletResponse response) throws Exception {
        return handler(Contact.class, request);
    }

    public ModelAndView accountsHandler(HttpServletRequest request, HttpServletResponse response) throws Exception {
        return handler(Account.class, request);
    }

    public ModelAndView groupsHandler(HttpServletRequest request, HttpServletResponse response) throws Exception {
        return handler(Group.class, request);
    }

    public ModelAndView entriesHandler(HttpServletRequest request, HttpServletResponse response) throws Exception {
        return handler(GroupEntry.class, request);
    }

}
